#include "SystemMemBuffer.h"
#include "NezhaException.h"

namespace Nezha
{

	SystemMemBuffer::SystemMemBuffer()
		:mData(NULL)
		,mSizeInBytes(0)
	{

	}

	SystemMemBuffer::~SystemMemBuffer()
	{
		resize(0);
	}

	u32 SystemMemBuffer::read(SystemMemBuffer* buf, u32 length, u32 offset /* = 0 */) const
	{
		return buf ? read(buf->mData, length, offset) : 0;
	}

	u32 SystemMemBuffer::read(SystemMemBuffer* buf, u32 bufOffset, u32 length, u32 offset /* = 0 */) const
	{
		if(buf)
		{
			if(bufOffset >= buf->mSizeInBytes - 1)
			{
				return 0;
			}

			u32 bufLen = buf->mSizeInBytes - bufOffset;
			length = length > bufLen ? bufLen : length;

			return read(buf->mData, length, offset);
		}

		return 0;
	}

	u32 SystemMemBuffer::read(u8* buf, u32 length, u32 offset /* = 0 */) const
	{
		if(offset >= mSizeInBytes - 1 || !buf)
		{
			return 0;
		}

		u32 readBytes = length > mSizeInBytes - offset ? mSizeInBytes - offset : length;
		memcpy(buf, (const void*)(mData + offset), readBytes);

		return readBytes;
	}

	u32 SystemMemBuffer::extendBuffer(SystemMemBuffer* buf)
	{
		return buf ? extendBuffer(buf->mData, buf->sizeInBytes()) : 0;
	}

	u32 SystemMemBuffer::extendBuffer(u8* data, u32 length)
	{
		if(!data)
		{
			return 0;
		}

		u32 newLen = length + mSizeInBytes;
		mData = mData ? (u8*)NZRealloc(mData, newLen) : (u8*)NZMalloc(newLen);
		memcpy((void*)(mData + mSizeInBytes), data, length);
		mSizeInBytes = newLen;

		return mSizeInBytes;
	}

	u32 SystemMemBuffer::write(u8* data, u32 length, u32 offset /* = 0 */)
	{
		if(offset >= mSizeInBytes - 1 || !data)
		{
			return 0;
		}

		u32 wroteBytes = length > mSizeInBytes - offset ? mSizeInBytes - offset : length;
		memcpy((void*)(mData + offset), data, wroteBytes);

		return wroteBytes;
	}

	SystemMemBuffer* SystemMemBuffer::clone() const
	{
		SystemMemBuffer* newBuf = NZ_New SystemMemBuffer();
		newBuf->resize(mSizeInBytes);
		newBuf->write(mData, mSizeInBytes);

		return newBuf;
	}

	void SystemMemBuffer::resize(u32 sizeInBytes)
	{
		if(mSizeInBytes != sizeInBytes)
		{
			if(mData)
			{
				NZFree(mData);
			}

			if(sizeInBytes > 0)
			{
				mData = (u8*)NZMalloc(sizeInBytes);
				memset(mData, 0, sizeInBytes);
				mSizeInBytes = sizeInBytes;
			}
			else
			{
				mData = NULL;
				sizeInBytes = 0;
			}
		}
	}


	SystemMemBufferShared::SystemMemBufferShared()
		:mLengthInBytes(0)
		,mOffset(0)
	{

	}

	SystemMemBufferShared::SystemMemBufferShared(u32 offset, u32 sizeInBytes, const SystemMemBufferPtr& buf)
	{
		NZ_ASSERT(buf.ptr());
		NZ_ASSERT(offset < buf->sizeInBytes());
		NZ_ASSERT(sizeInBytes <= (buf->sizeInBytes() - offset));

		mLengthInBytes = sizeInBytes;
		mOffset = offset;
		mSysMemBuffer = buf;
	}

	SystemMemBufferShared::~SystemMemBufferShared()
	{
		mSysMemBuffer = NULL;
	}

	void SystemMemBufferShared::reset(u32 offset, u32 sizeInBytes, const SystemMemBufferPtr& buf)
	{
		NZ_ASSERT(buf.ptr());
		NZ_ASSERT(offset < buf->sizeInBytes());
		NZ_ASSERT(sizeInBytes <= (buf->sizeInBytes() - offset));

		mLengthInBytes = sizeInBytes;
		mOffset = offset;
		mSysMemBuffer = buf;
	}

	u32 SystemMemBufferShared::write(u8* data, u32 length, u32 offset /* = 0 */)
	{
		NZ_ASSERT(length + offset > mLengthInBytes);

		return mSysMemBuffer->write(data, length, offset + mOffset);
	}
}